package edu.pg.ipse.kb.frame.core;

import edu.pg.ipse.utils.Utils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 */
public class DefaultFrameAccessObject implements IFrameAccessObject {

    private final Map<Object, Frame> frames = new HashMap<Object, Frame>();

    public DefaultFrameAccessObject() {
        buildKnowledgeBase();
    }

    public Frame getFrame(Object key) {
        if (key != null) {
            Frame frame = frames.get(key);
            frame.setFrameAccessObject(this);
            return frame;
        } else {
            return null;
        }
    }

    private void buildKnowledgeBase() {

        // Характеристики параллельной реализации
        Frame charParallelImplFrame = createEfficienceFrame();
        frames.put(charParallelImplFrame.getId(), charParallelImplFrame);

        // Параллельная реализация сумматора
        Frame summParallelImplFrame = new Frame(2, "Параллельная реализация сумматора");
        Slot charSlot = new Slot("Характеристики");
        charSlot.setType(SlotType.FRAME);
        charSlot.setValue(charParallelImplFrame);
        summParallelImplFrame.addSlot(charSlot);
        frames.put(summParallelImplFrame.getId(), summParallelImplFrame);

        // Закон Амдала
        Frame amdahlLawFrame = createAmdahlLawFrame();
        frames.put(amdahlLawFrame.getId(), amdahlLawFrame);

        // Эффективное выполнение
        Frame effectiveExecutionFrame = new Frame(4, "Эффективное выполнение");
        effectiveExecutionFrame.setDescr(
                "В качестве критерия эффективности используется закон Амдала:" +
                "Будем считать, что вариант распараллеливания эффективным " +
                "если оценочное ускорение будет меньше ограничения по закону " +
                "Амдала менее чем в 2 раза");
        Slot effectiveExecutionSlot = new Slot("Критерий эффективности");
        effectiveExecutionSlot.setType(SlotType.BOOL);
        Demon effectiveExecutionDemon = new Demon() {

            public Object execute(List<Object> params) {

                Frame characteristicFrame =
                        this.slot.getFrame().getFrameAccessObject().getFrame(1);
                Double speedUp = 0.0;
                for (Slot tmpSlot : characteristicFrame.getSlots()) {
                    if(tmpSlot.getName().equals("Ускорение")) {
                        speedUp = (Double) tmpSlot.getValue();
                        break;
                    }
                }

                Frame amdahlLawFrame =
                        this.slot.getFrame().getFrameAccessObject().getFrame(3);
                Double amdahlSpeedUp = 0.0;
                for (Slot tmpSlot : amdahlLawFrame.getSlots()) {
                    if(tmpSlot.getName().equals("Ограничение")) {
                        amdahlSpeedUp = (Double) tmpSlot.getValue();
                        break;
                    }
                }

                return amdahlSpeedUp / speedUp < 2;
            }
        };
        effectiveExecutionSlot.addDemon(effectiveExecutionDemon);
        effectiveExecutionFrame.addSlot(effectiveExecutionSlot);
        frames.put(effectiveExecutionFrame.getId(), effectiveExecutionFrame);

    }

    private Frame createEfficienceFrame() {

        Frame charParallelImpl = new Frame(1, "Характеристики параллельной реализации");

        Slot speedUp = new Slot("Ускорение");
        Demon speedUpDemon = new Demon() {

            public Object execute(List<Object> params) {

                Integer p = (Integer) Context.getInstance().
                        getParameter(Constants.Parameters.PROCESSERS_COUNT);
                Integer n = (Integer) Context.getInstance().
                        getParameter(Constants.Parameters.ARRAY_LENGTH);

                double pReq = n / Utils.log(n.doubleValue(), 2);
                double sReg = (n - 1) / (2 * Utils.log(n.doubleValue(), 2));

                if(p.intValue() > pReq) {
                    return new Double(sReg);
                } else {
                    return new Double(sReg * p.doubleValue() / pReq);
                }
            }
        };
        speedUp.addDemon(speedUpDemon);
        charParallelImpl.addSlot(speedUp);

        Slot alpha = new Slot("Доля последовательных вычислений");
        Demon alphaDemon = new Demon() {
            public Object execute(List<Object> params) {

                Integer p = (Integer) Context.getInstance().
                        getParameter(Constants.Parameters.PROCESSERS_COUNT);
                Integer n = (Integer) Context.getInstance().
                        getParameter(Constants.Parameters.ARRAY_LENGTH);

                return 1/p + (Utils.log(p.doubleValue(), 2) / n);
            }
        };
        alpha.addDemon(alphaDemon);
        charParallelImpl.addSlot(alpha);

        Slot efficiency = new Slot("Эффективность");
        Demon efficiencyDemon = new Demon() {

            public Object execute(List<Object> params) {
                Integer p = (Integer) Context.getInstance().
                        getParameter(Constants.Parameters.PROCESSERS_COUNT);
                Integer n = (Integer) Context.getInstance().
                        getParameter(Constants.Parameters.ARRAY_LENGTH);

                double pReq = n / Utils.log(n.doubleValue(), 2);
                double sReg = (n - 1) / (2 * Utils.log(n.doubleValue(), 2));

                if(p.intValue() > pReq) {
                    return new Double(sReg / p);
                } else {
                    return new Double(sReg / pReq);
                }
            }
        };
        efficiency.addDemon(efficiencyDemon);
        charParallelImpl.addSlot(efficiency);

        return charParallelImpl;
    }

    private Frame createAmdahlLawFrame() {

        Frame amdahlLawFrame = new Frame(3, "Закон Амдала");
        amdahlLawFrame.setDescr(
                "Иллюстрирует ограничение роста производительности " +
                "вычислительной системы с увеличением количества вычислителей. " +
                "Согласно этому закону, ускорение выполнения программы за счет " +
                "распараллеливания её инструкций на множестве вычислителей " +
                "ограничено временем, необходимым для выполнения её " +
                "последовательных инструкций");
        Slot formulaSlot = new Slot("Ограничение");
        Demon formulaDemon = new Demon () {

            public Object execute(List<Object> params) {

                Integer p = (Integer) Context.getInstance().
                        getParameter(Constants.Parameters.PROCESSERS_COUNT);
                Frame characteristicFrame =
                        this.slot.getFrame().getFrameAccessObject().getFrame(1);
                Double alpha = 0.0;
                for (Slot tmpSlot : characteristicFrame.getSlots()) {
                    if(tmpSlot.getName().equals("Доля последовательных вычислений")) {
                        alpha = (Double) tmpSlot.getValue();
                        break;
                    }
                }
                return new Double(1 / (alpha + (1 - alpha) / p));
            }
        };
        formulaSlot.addDemon(formulaDemon);
        amdahlLawFrame.addSlot(formulaSlot);

        return amdahlLawFrame;
    }
        
}
